/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package crypbox.net.cryptograhy;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

/**
 *
 * @author m-jay
 */
public class Cryptograph implements Serializable {

    private static Cryptograph instance = null;
    private SecretKey secretKey = null;
    private static final String algorithm = "AES";
    private static final int bitGroesse = 128;
    private static final boolean neuerSchluessel = false;
    private static final String ENCRYPTION_HEADER = "----cryptboxheader----";

    private Cryptograph() throws Exception {
        if (secretKey == null) {
            createNewKey();
        }
    }

    public void createNewKey() throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(algorithm);
        kg.init(bitGroesse);
        secretKey = kg.generateKey();

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object"));
        oos.writeObject(this);
        oos.close();
    }

    public static Cryptograph getInstance() {
        if (instance == null) {
            try {
                try {
                    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object"));
                    Object o = ois.readObject();
                    if (o instanceof Cryptograph) {

                        Cryptograph cryp = (Cryptograph) o;
                        if (cryp.secretKey.getAlgorithm().equals(algorithm) && !neuerSchluessel) {
                            instance = (Cryptograph) o;
                        }
                    }
                } catch (Exception ex) {
                    //badda bumm
                }

                if (instance == null) {
                    instance = new Cryptograph();
                }


            } catch (Exception ex) {
                Logger.getLogger(Cryptograph.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        //System.out.println(instance.keyPair.getPrivate());
        return instance;
    }

    public void encryptFile(File regularFile, File encryptedFile) throws Exception {

        Cipher cipher = Cipher.getInstance(algorithm);

        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        FileInputStream fis = new FileInputStream(regularFile);
        FileOutputStream fos = new FileOutputStream(encryptedFile);
        //fos.write(ENCRYPTION_HEADER.getBytes());
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);
        byte[] block = new byte[4096];
        int i;
        while ((i = fis.read(block)) != -1) {
            cos.write(block, 0, i);
        }

        fis.close();

        cos.close();
    }

    public void decryptFile(File decryptedFile, File encryptedFile) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        FileInputStream fis = new FileInputStream(encryptedFile);
//        byte[] testBuffer = new byte[ENCRYPTION_HEADER.getBytes().length];
        FileOutputStream fos = new FileOutputStream(decryptedFile);

//        int length = fis.read(testBuffer);
//        if (new String(testBuffer).equals(ENCRYPTION_HEADER)) {

            CipherInputStream cis = new CipherInputStream(fis, cipher);


            byte[] block = new byte[4096];
            int i;
            while ((i = cis.read(block)) != -1) {
                fos.write(block, 0, i);
            }
            cis.close();
            fos.close();
        }
//    else {
//            if (length != -1) {
//                fos.write(testBuffer, 0, length);
//                byte[] buffer = new byte[4096];
//                while ((length = fis.read(buffer)) != -1) {
//                    fos.write(buffer, 0, length);
//                }
//            }
//        }
//    }

    public byte[] encryptBytes(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = Cipher.getInstance(algorithm);

        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] doFinal = cipher.doFinal(bytes);
//        byte[] result = new byte[ENCRYPTION_HEADER.getBytes().length + doFinal.length];
//        for(int i = 0; i < ENCRYPTION_HEADER.getBytes().length;i++){
//            result[i] = ENCRYPTION_HEADER.getBytes()[i];
//        }
//        for(int i = ENCRYPTION_HEADER.getBytes().length; i < result.length; i++){
//            result[i] = doFinal[i-ENCRYPTION_HEADER.getBytes().length];
//        }
        return doFinal;
    }

    public byte[] decryptBytes(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = Cipher.getInstance(algorithm);

        cipher.init(Cipher.DECRYPT_MODE, secretKey);

        return cipher.doFinal(bytes);
    }

    public SecretKey getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(SecretKey secretKey) {
        this.secretKey = secretKey;
    }

    public void encryptFolder(File decryptedFolder, File destinationfolder) {
        encryptFolder(decryptedFolder, destinationfolder, decryptedFolder);
    }

    private void encryptFolder(File decryptedFolder, File destinationfolder, File toBeEncrypted) {

        if (toBeEncrypted.isDirectory()) {

            File newFile = new File(toBeEncrypted.getAbsolutePath().replaceFirst(decryptedFolder.getAbsolutePath(), destinationfolder.getAbsolutePath()));
            newFile.mkdirs();
            File[] listFiles = toBeEncrypted.listFiles();
            for (File subFile : listFiles) {
                encryptFolder(decryptedFolder, destinationfolder, subFile);
            }
        } else {
            String s = toBeEncrypted.getParentFile().getAbsolutePath().replaceFirst(decryptedFolder.getAbsolutePath(), destinationfolder.getAbsolutePath());
            File parent = new File(s);
            parent.mkdirs();
            File file = new File(parent, toBeEncrypted.getName());
            try {
                file.createNewFile();
                encryptFile(toBeEncrypted, file);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

    }
}
